#pragma once

using namespace System;
using namespace System::Collections::Generic;
using namespace Microsoft::Win32::SafeHandles;

namespace MaxSMART {
	public ref class ScsiAddress {
	private:
		initonly Byte _portNumber;
		initonly Byte _pathId;
		initonly Byte _targetId;
		initonly Byte _lun;
	public:
		property Byte PortNumber { Byte get() { return _portNumber; } }
		property Byte PathId { Byte get() { return _pathId; } }
		property Byte TargetId { Byte get() { return _targetId; } }
		property Byte Lun { Byte get() { return _lun; } }
		ScsiAddress(Byte portNumber, Byte pathId, Byte targetId, Byte lun)
			:_portNumber(portNumber), _pathId(pathId), _targetId(targetId), _lun(lun) { }
	};

	public enum class BusType {
		Unknown = 0x00,
		Scsi,
		Atapi,
		Ata,
		Ieee1394,
		Ssa,
		Fibre,
		Usb,
		RAID,
		iScsi,
		Sas,
		Sata,
		Sd,
		Mmc,
		Virtual,
		FileBackedVirtual,
		Max,
		MaxReserved = 0x7F
	};

	public enum class SmartAttribute : Byte {
		ReadErrorRate = 0x01,
		ThroughputPerformance = 0x02,
		SpinUpTime = 0x03,
		StartStopCount = 0x04,
		ReallocateSectorCount = 0x05,
		ReadChannelMargin = 0x06,
		SeekErrorRate = 0x07,
		SeekTimePerformance = 0x08,
		PowerOnHourCount = 0x09,
		SpinRetryCount = 0x0A,
		RecalibrationCount = 0x0B,
		PowerCycleCount = 0x0C,
		CommandTimeout = 0xBC,
		Temperature = 0xC2,
		ReallocationEventCount = 0xC4,
		CurrentPendingSectorCount = 0xC5,
		UncorrectableSectorCount = 0xC6,
		UltraDMACrcErrorCount = 0xC7,
		SoftReadErrorRate = 0xC9,
	};

	[Flags]
	public enum class SmartAttributeFlags : UInt16 {
		PreFailure = 0x01,
		OnlineCollection = 0x02,
		Performance = 0x04,
		ErrorRate = 0x08,
		EventCount = 0x10,
		SelfPreserving = 0x20,
	};

	public enum class SmartCommand : Byte {
		IdentifyDevice,
		EnableSmart,
		DisableSmart,
		ReadAttributes,
		ReadThresholds,
	};

	public value class SmartAttributeData {
	public:
		SmartAttributeFlags Flags;
		Byte Value;
		Byte Peak;
		UInt16 Raw1;
		UInt16 Raw2;
		UInt16 Raw3;
		Byte Reserved;
	};

	[Flags]
	public enum class SmartMethod : Byte {
		Smart = 0x01,
		ScsiMiniport = 0x02,
		Storage = 0x04,
	};

	typedef struct IDENTIFY_DATA *PIDENTIFY_DATA;

	public ref class SmartDrive sealed
	{
	public:
		static array<SmartDrive^>^ EnumeratePhysicalDrives();
	private:
		initonly String^ _name;
		initonly BusType _busType;
		initonly SafeFileHandle^ Handle;
		initonly ScsiAddress^ _scsiAddress;
		initonly bool _hasRights;
		//String^ _model;
		//String^ _serial;
		initonly Dictionary<SmartAttribute, SmartAttributeData>^ _attributes;
		initonly Dictionary<SmartAttribute, Byte>^ _thresholds;
		SmartDrive(SafeFileHandle^ handle, String^ name, bool hasRights);
	public:
		virtual ~SmartDrive();
	public:
		property String^ Name { String^ get() { return _name; } }
		property Byte Index;
		property BusType BusType { MaxSMART::BusType get() { return _busType; } }
		property ScsiAddress^ ScsiAddress { MaxSMART::ScsiAddress^ get() { return _scsiAddress; } }
		property bool HasRights { bool get() {return _hasRights; } }
		property String^ Model;// { String^ get() { return _model; } }
		property String^ Serial;// { String^ get() { return _serial; } }
		property Dictionary<SmartAttribute, SmartAttributeData>^ Attributes { Dictionary<SmartAttribute, SmartAttributeData>^ get() { return _attributes; } }
		property Dictionary<SmartAttribute, Byte>^ Thresholds { Dictionary<SmartAttribute, Byte>^ get() { return _thresholds; } }
		bool ExecuteCommand(SmartMethod method, SmartCommand command);
	private:
		void UpdateIdentifyData(PIDENTIFY_DATA data);
		bool UpdateAttributes(PBYTE buffer, DWORD length);
		bool UpdateThresholds(PBYTE buffer, DWORD length);
		bool ExecuteSmart(SmartCommand command);
		bool ExecuteScsiMiniport(SmartCommand command);
		bool ExecuteStoragePredictFailure();
		bool ExecuteStorageQueryProperty(MaxSMART::BusType% busType);
	};

}
